源码相关 内容 概述 问题记录 成本流程 表和字段 函数 单据 流程 报表 源码 MCostElement.java Mcost.java MCostDetail.java 概述 从代码层来看看成本相关原理 参考:Oracle EBS i11 成本管理系统 操作手册 https://wenku.baidu.com/view/93ade1d09f3143323968011ca300a6c30d22f152.html 问题记录 1. 成本要素太多,导致汇总数据无意义。 个人倾向使用报表来手工汇总成本要素,毕竟成本要素可以帮助成本管理。 成本流程 1. 设置成本类型(重要概念,一个账套对应一个成本类型(存货计价集合)) 2. 计价方式(可以为空)、成本要素(关键概念,特别是材料类型) 3. 创建材料成本记录 4. 更新材料成本,创建成本明细,成本队列 5. 成本调整 6. 其他成本,比如到岸成本(可选择其他成本类型,虽然系统默认为材料类型) 7. 检查成本吸收账户(通过费用灵活映射) 实体新建后,会有一个默认的成本类型(缺省)和成本要素(类型=材料,计价=标准成本)。 默认产品的成本表(Mcost)成本价格为0,成本明细(MCostDetail)无记录。 创建初始成本有2种方法: 1. 直接导入库存时,直接更新MCost表的当前成本。(不推荐,因为直接更新表字段,无相关单据追溯信息) 2. 新建成本相关单据并过账,比如从PO到MR,或者成本调整单 2.1 已测试标准成本,新建PO,新建MR过账2次,系统新建所有成本要素的当前成本=PO的采购成本字段值 2.2 已测试标准成本的某个成本要素,新建成本调整单过账1次,系统新建该产品的当前成本=成本调整明细成本 3. 推荐2.2方法初始化成本, 3.1 当新产品无库存时,过账不会生成分录,但是可更新产品的成本元素的成本值。 3.2 成本记录可以看到成本变动明细,且保留原始单据索引 3.3 一次只能更新一种成本元素,如果用户有多种成本元素,需要多次初始化更新。 表和字段 ####==查看cost相关的表==#### SELECT tablename,isview FROM ad_table WHERE UPPER(name) ~ 'COST' AND entitytype='D' ORDER BY 2,1 结果:14 C_LandedCost,N----------------------到岸成本 C_LandedCostAllocation,N----------到岸成本分配 C_OrderLandedCost,N---------------订单到岸成本暂估 C_OrderLandedCostAllocation,N----订单到岸成本暂估分配 M_Cost,N------------------------------成本表 M_CostDetail,N-----------------------成本明细表(一个单据明细可影响多个成本要素) M_CostElement,N---------------------成本要素表 M_CostHistory,N----------------------成本历史 M_CostQueue,N-----------------------成本队列,只针对于FIFO或者LILO计价 M_CostType,N-------------------------成本类型 M_CostMovement_v,Y----------------成本变动(视图) RV_Cost,Y------------------------------成本表(视图) RV_CostDetail,Y------------------------成本明细表(视图) RV_CostSummary,---------------------成本汇总(视图) 表内字段相关逻辑,参考:M表名.java。 视图直接看sql语句。 ####==查看cost相关的窗口,页签==#### select a.name , b.name, c.tablename from ad_window a join ad_tab b on a.ad_window_id=b.ad_window_id join ad_table c on b.ad_table_id=c.ad_table_id where upper(c.name) ~ 'COST' order by 1,3 结果:11 Cost Element,Cost Element,M_CostElement Cost Type,Cost Type,M_CostType Invoice (Vendor),Landed Costs,C_LandedCost Invoice (Vendor),Landed Cost Allocation,C_LandedCostAllocation Product,Costs,M_Cost Product Costs,Product Costs,M_Cost Product Costs,Cost Details,M_CostDetail Product Costs,Cost Movement,M_CostMovement_v Product Costs,Cost Queue,M_CostQueue Purchase Order,Estimated Landed Cost,C_OrderLandedCost Purchase Order,Estimated Landed Cost Allocation,C_OrderLandedCostAllocation ####==查看成本要素相关的窗口,页签==#### select a.name , b.name, c.tablename from ad_window a join ad_tab b on a.ad_window_id=b.ad_window_id join ad_table c on b.ad_table_id=c.ad_table_id join ad_column d on c.ad_table_id=d.ad_table_id join ad_element e on e.ad_element_id=d.ad_element_id where e.columnname ~ 'M_CostElement_ID' order by 1,3 结果:9 Product,Costs,M_Cost Product Costs,Product Costs,M_Cost Product Costs,Cost Details,M_CostDetail Product Costs,Cost Movement,M_CostMovement_v Product Costs,Cost Queue,M_CostQueue Cost Element,Cost Element,M_CostElement Invoice (Vendor),Landed Costs,C_LandedCost Invoice (Vendor),Landed Cost Allocation,C_LandedCostAllocation Purchase Order,Estimated Landed Cost,C_OrderLandedCost 除了常见的成本要素材料类型,从到岸成本可以指定“成本要素”,给出了其他成本要素的单据处理思路,比如“制造工时”“制造费用”等等。 制造成本的记录和结转,类似于到岸成本。 ####==查看成本相关流程的位置==#### select a.name as prc_name, a.description as prc_desc, h.name as menu_name, b.name as col_name, c.tablename, c.entitytype, d.name as tab_name, e.name as fld_name, e.isdisplayed, f.name as tb_name, g.name as win_name from ad_process a left join ad_column b on a.ad_process_id=b.ad_process_id left join ad_table c on b.ad_table_id=c.ad_table_id left join ad_tab d on c.ad_table_id=d.ad_table_id left join ad_field e on d.ad_tab_id = e.ad_tab_id and b.ad_column_id=e.ad_column_id left join ad_toolbarbutton f on d.ad_tab_id=f.ad_tab_id /* 检查tab按钮是否挂流程 */ left join ad_window g on d.ad_window_id=g.ad_window_id left join ad_menu h on a.ad_process_id=h.ad_process_id where a.value ~'Cost' and a.IsReport='N' order by h.name,e.isdisplayed desc 结果:9 (8个菜单链接,1个窗口按钮) Invoice (Vendor),Landed Costs,,Distribute Costs,C_LandedCost,Process Now,Distribute Costs ####==查看成本要素的代码==#### 1. isCostFrozen:无代码 2. isCalculated: 成本要素,默认“isCalculated”= false,表示单据可以更新该成本要素。 MCostElement.MCostElement (org.adempiere.base\src\org\compiere\model) Line254 MCostElement.beforeSave L296 有一段注释掉的代码 3. COSTELEMENTTYPE_Material:存货核算都使用成本元素的“材料”类型 其他成本元素类型,参考“到岸成本” 函数 ####==查看cost相关的函数==#### SELECT pg_proc.proname AS "函数名称", pg_type.typname AS "返回值数据类型", pg_proc.pronargs AS "参数个数" FROM pg_proc JOIN pg_type ON (pg_proc.prorettype = pg_type.oid) WHERE pg_type.typname != 'void' AND Upper(pg_proc.proname) ~ 'COST' AND pronamespace = (SELECT pg_namespace.oid FROM pg_namespace WHERE nspname = 'adempiere'); 结果:0 单据 什么单据会影响成本计算?(CostUpdate.java ) 什么单据取值于成本表?(MCost.java ) M_CostDetail表会记录如下单据明细: 订单:c_orderline_id 收发单:m_inoutline_id 发票:c_invoiceline_id 移库:m_movementline_id 盘库/领用/成本调整:m_inventoryline_id 简单生产:m_productionline_id 项目投放:c_projectissue_id ####==查看id的基本单据==#### select b.ad_reference_id, b.name as ad_reference_name, a.name as ad_ref_list_name , c.name from ad_ref_list a join ad_reference b on a.ad_reference_id=b.ad_reference_id join ad_ref_list_trl c on a.ad_ref_list_id = c.ad_ref_list_id where b.name ~'DocBaseType' order by 3 结果: 32 183,C_DocType DocBaseType,AP Credit Memo,应付贷项通知单 183,C_DocType DocBaseType,AP Invoice,应付发票 183,C_DocType DocBaseType,AP Payment,付款单 183,C_DocType DocBaseType,AR Credit Memo,应收贷项通知单 183,C_DocType DocBaseType,AR Invoice,应收发票 183,C_DocType DocBaseType,AR Pro Forma Invoice,应收形式发票 183,C_DocType DocBaseType,AR Receipt,收款单 183,C_DocType DocBaseType,Bank Statement,银行对账单 183,C_DocType DocBaseType,Cash Journal,现金日记账 183,C_DocType DocBaseType,Distribution Order,配送单 183,C_DocType DocBaseType,Fixed Assets Addition,资产新增单 183,C_DocType DocBaseType,Fixed Assets Depreciation,资产折旧单 183,C_DocType DocBaseType,Fixed Assets Disposal,资产处置单 183,C_DocType DocBaseType,GL Document,总账单据 183,C_DocType DocBaseType,GL Journal,凭证 183,C_DocType DocBaseType,Maintenance Order,维护工单 183,C_DocType DocBaseType,Manufacturing Cost Collector,制造成本归集单 183,C_DocType DocBaseType,Manufacturing Order,生产工单 183,C_DocType DocBaseType,Match Invoice,发票匹配单 183,C_DocType DocBaseType,Match PO,采购匹配单 183,C_DocType DocBaseType,Material Delivery,发货单 183,C_DocType DocBaseType,Material Movement,移库单 183,C_DocType DocBaseType,Material Physical Inventory,盘库单/领用单/成本调整单 183,C_DocType DocBaseType,Material Production,材料生产单 183,C_DocType DocBaseType,Material Receipt,收货单 183,C_DocType DocBaseType,Payment Allocation,支付分配单 183,C_DocType DocBaseType,Payroll,薪酬单 183,C_DocType DocBaseType,Project Issue,项目投放单 183,C_DocType DocBaseType,Purchase Order,采购订单 183,C_DocType DocBaseType,Purchase Requisition,请购单 183,C_DocType DocBaseType,Quality Order,质检单 183,C_DocType DocBaseType,Sales Order,销售订单 流程 ####==3个流程==#### 什么流程会创建成本记录?(CostCreate.java ) 什么流程会更新成本?(CostUpdate.java ) 什么流程会调整成本?(CostAdjustmentLineRefreshCost.java ) ###==CostCreate.java==#### 说明:调用processProduct方法来创建单个产品的成本记录 参考:MCostDeatil.processProduct ###==CostUpdate.java==#### 说明:在账套的成本类型内,进行成本要素更新 疑问:无法指定成本类型,是否需要增加该参数 ###==CostAdjustmentLineRefreshCost.java==#### 说明: 报表 系统自带的3个成本报表: 1. 成本要素 2. 成本汇总 3. 成本明细 源码 ####==5个核心源码文件==#### MCostElement.java (org.adempiere.base\src\org\compiere\model) MCost.java (org.adempiere.base\src\org\compiere\model) CostCreate.java (org.adempiere.base.process\src\org\compiere\process) CostUpdate.java (org.adempiere.base.process\src\org\compiere\process) CostAdjustmentLineRefreshCost.java (org.adempiere.base.process\src\org\idempiere\process) MCostElement.java ####==创建材料成本==#### 方法:getMaterialCostElement,L56,93,114 ####==获取材料成本==#### 方法:getCostingMethods,L148 ####==创建非材料成本==#### 方法:getNonCostingMethods,L167 说明:计价方式=NULL,使用百分比成本 ####==新建成本要素==#### 方法:beforeSave,L274 说明:元素类型=材料 or 外包,其计价方式不能有重复记录,比如一个组织的成本要素清单为: 要素名称 成本要素类型 计价方式 材料 材料 标准计价 (只能有一条) 包装 材料 标准计价 (如果再来一条,就是错误) 外包 外包 标准计价 (只能有一条) 外包2 外包 标准计价 (如果再来一条,就是错误) 费用1 材料间接费用 移动平均 费用2 材料间接费用 移动平均 费用3 制造费用 标准计价 费用4 制造费用 标准计价 资源1 资源 标准计价 资源2 资源 标准计价 Mcost.java ####==创建成本记录==#### 方法1:create (MClient client),Line631 说明:为该实体下的每个产品创建每个成本记录 步骤: 1. 使用getCurrentCost方法1 2. 再调用M_CostDetail.processProduct 方法2:create (MProduct product),Line693 说明:为该产品创建标准成本 步骤: 1. 如果成本层级=实体,Mcost.createCostingRecord 2. 如果成本层级=组织,Mcost.createForChildOrg 3. 如果成本层级=批次,警告:无标准成本创建 ####==成本取值==#### 注意:关于过账时候,提示“No Costs for xxxxx”导致过账错误 原因:产品为标准成本,第一次采购、入库时会报错 解决:再过账一次 1. 搜索“No Costs for”找到如下代码 Doc_InOut.createFacts (org.adempiere.base\src\org\compiere\acct) L227,418,605,625,820 Doc_Inventory.createFacts (org.adempiere.base\src\org\compiere\acct) L280 Doc_MatchPO.createFacts (org.adempiere.base\src\org\compiere\acct) L324 Doc_Production.createFacts (org.adempiere.base\src\org\compiere\acct) L416 M_PriceList_Create.doIt (org.adempiere.base.process\src\org\compiere\process) L714(警告) 2. 出错条件: if (costs == null || costs.signum() == 0) 3. 新建产品后,默认MCost表会新建一个记录,cost=0 4. 如果标准成本,第一次过账会报错,再过账一次OK,参考getCurrentCost() 方法1 5. 如果移动平均,不会出现过账错误 方法1:getCurrentCost (MProduct product, int M_AttributeSetInstance_ID, MAcctSchema as, int AD_Org_ID, String costingMethod, BigDecimal qty, int C_OrderLine_ID, boolean zeroCostsOK, String trxName),Line77 说明:取回/创建当前成本价格 步骤: 1. 先从MCostDetail处理相关单据得到成本值(亦即cost的创建时需要从MCostDetail处理),L104 1.1 使用MCostDetail.processProduct (product, trxName)方法,L672 1.2 继续调用MCostDetail.Process( ) ,Line 825 1.3 对于标准成本,实际调用MCost.getSeedCosts来赋值第一次成本值,见下节 2. 返回该成本值,L106 方法2:getCurrentCost (MProduct product, int M_ASI_ID, MAcctSchema as, int Org_ID, int M_CostType_ID, String costingMethod, BigDecimal qty, int C_OrderLine_ID, boolean zeroCostsOK, String trxName),Line126 说明:比方法1多了一个M_CostType_ID参数,从成本层级来获取当前成本价格和累计成本 步骤: 1. 查询成本表(M_cost) 2. 材料成本 2.1 如果StandardCosting,材料成本累计=当前成本*当前数量 2.2 如果Fifo || Lifo,材料成本累计=当前序列成本*当前序列数量 3. 其他成本(成本要素=NULL的自定义元素???) 3.1 材料成本累计=当前成本*当前数量 4. 累计成本总额 4.1 成本百分比:如果成要素的成本计价方式=x(列默认值),可设置成本总额百分比 4.1 累计成本=材料成本+其他成本+成本百分比 ####==成本赋值顺序==#### getSeedCosts(MProduct product, int M_ASI_ID,MAcctSchema as, int Org_ID, String costingMethod, int C_OrderLine_ID),Line269-451 Step1: 待续 COSTINGMETHOD_AverageInvoice 计算如 COSTINGMETHOD_AveragePO 计算如 COSTINGMETHOD_Fifo 取值如 COSTINGMETHOD_Lifo 取值如 COSTINGMETHOD_LastInvoice 取上次发票金额 COSTINGMETHOD_LastPOPrice 取本次订单或者上次订单的采购金额 COSTINGMETHOD_StandardCosting 取值如 COSTINGMETHOD_UserDefined 取值如 Step2: 2.1 如果订单id真,取本次订单明细的采购成本字段值,L304 2.2 非标准成本计价,取标准成本的材料成本,L314 2.3 如果没有价格(采购订单优先) 2.3.1 如果LastPOPrice || StandardCosting,取上次采购订单的采购金额 2.3.2 其他计价,取上次发票金额 2.4 如果还没有价格(发票优先) 2.4.1 如果LastPOPrice || StandardCosting,取上次发票金额 2.4.2 其他计价,取上次采购订单的采购金额 2.5 如果还没有价格 2.5.1 取产品的采购页签的采购价格(M_Product_PO.PricePO), 2.5.2 如果该值为0,取采购页签的列表价格(M_Product_PO.PriceList) 2.6 如果还没有价格 2.6.1 取产品的采购价格表的标准价格(M_PriceList_Version.priceStd) 2.6.2 如果该值为0,取采购价格表的列表价格(M_PriceList_Version.priceList) ####==成本计算==#### 待续  MCostDetail.java 当完成如下单据时,会创建成本明细 Create New Cost Detail for Purchase Orders,Called from Doc_MatchPO Create New Cost Detail for AP Invoices,Called from Doc_Invoice - for Invoice Adjustments Create New Cost Detail for SO Shipments,Called from Doc_MInOut - for SO Shipments Create New Cost Detail for Physical Inventory,Called from Doc_Inventory Create New Cost Detail for Movements,Called from Doc_Movement Create New Cost Detail for Production,Called from Doc_Production Create New Cost Detail for Match Invoice,Called from Doc_MatchInvoice Create New Cost Detail for Project Issue,Called from Doc_ProjectIssue ####==处理产品成本明细==#### 方法:processProduct (MProduct product, String trxName) 说明:Process Cost Details for product 步骤:产品的未处理的成本明细,调用MCostDetail.Process ( ) 方法,处理并保存。 参考:CostCreate流程 1. 当某个产品的单据过账时候也会触发getCurrentCost()方法1,从而创建成本记录 2. 单独使用CostCreate流程也是调用processProduct方法来创建单个产品的成本记录 ####==处理成本明细==#### 方法:MCostDetail.process (MAcctSchema as, MProduct product, MCostElement ce, int Org_ID, int M_ASI_ID) 说明:Process Cost Detail Record,The record is saved if processed. 步骤: 1. 如果是移动平均计价,则返回true(已处理) 2. 从MCost表取成本值,并复制到MCostDetail表 3. 保存该cost到M_CostHistory表 4. 处理特殊单据=成本调整单,price=amt / qty 5. 处理Purchase Order的明细记录 5.1 isAveragePO, 那么 cost.setWeightedAverage(amt, qty) 5.2 isLastPOPrice, 那么 cost.setCurrentCostPrice(price) 5.3 isStandardCosting, 那么 cost.setCurrentCostPrice(MCost.getSeedCosts(.....)),参考上节方法 6. 处理AP Invoice的明细记录 6.1 isAverageInvoice, 那么 cost.setWeightedAverage(amt, qty) 6.2 isAveragePO() && costAdjustment, 那么 cost.setWeightedAverage(amt, qty) 【同上?】 6.3 isFifo() || isLifo, 那么 cost.setCurrentCostPrice(MCostQueue[0].getCurrentCostPrice()) 6.4 isLastInvoice, 那么 cost.setCurrentCostPrice(price) 6.5 isStandardCosting, 那么 cost.setCurrentCostPrice(MCost.getSeedCosts(.....)),参考上节方法 7. 处理Qty Adjustment的明细记录 待续